﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.OpenGL.Native;
using Silvermoon.Drawing;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;

namespace Silvermoon.OpenGL
{
    public static class TextureUtil
    {

        public static byte[] BytesFromStream(Stream stream)
        {
            MemoryStream ms = stream as MemoryStream;
            if (ms != null) return ms.GetBuffer();

            int len = (int)stream.Length;
            byte[] result = new byte[len];
            stream.Read(result, 0, len);
            return result;
        }

        public static int ConvertToValidTextureDimension(int size)
        {
            int shiftAmount = 0;
            int minDim = size;
            while ((minDim >> 1) >= 1)
            {
                shiftAmount++;
                minDim >>= 1;
            }
            minDim <<= shiftAmount;
            if (minDim < size)
                minDim <<= 1;

            return minDim > 0 ? minDim : 1;
        }

        public static GlesPixelFormat GetGlFormatFromTextureFormat(TextureFormat textureFormat)
        {
            switch (textureFormat)
            {
                case TextureFormat.RGB555:
                case TextureFormat.RGB24:
                case TextureFormat.RGB565: return GlesPixelFormat.RGB;

                case TextureFormat.RGBA8888:
                case TextureFormat.RGBA4444:
                case TextureFormat.RGBA5551: return GlesPixelFormat.RGBA;


                case TextureFormat.Luminance: return GlesPixelFormat.Luminance;
                case TextureFormat.HiResAlpha: return GlesPixelFormat.Alpha;
                case TextureFormat.Alpha: return GlesPixelFormat.Alpha;

                case TextureFormat.LuminanceAlpha: return GlesPixelFormat.LuminanceAlpha;

                default: throw new NotSupportedException();
            }
        }

        internal static unsafe IntPtr ConvertRGBAToAlpha(BitmapImageData data)
        {
            int max = data.Width * data.Height - 1;
            byte* ptr = (byte*)data.Scan0;
            byte* copyPtr = ptr;
            for (int pos = 0; pos < max; pos++)
            {
                byte a = ptr[3];
                ptr += 4;
                copyPtr[0] = a;
                copyPtr++;
            }
            return data.Scan0;
        }

        internal static unsafe void ConvertRGBToLuminance(BitmapImageData data)
        {
            int max = data.Width * data.Height;
            byte* ptr = (byte*)data.Scan0;
            byte* copyPtr = ptr;
            for (int pos = 0; pos < max; pos++)
            {
                byte r = *ptr;
                ptr += 3;
                *copyPtr++ = r;
            }
        }

        internal static unsafe IntPtr ConvertRGBAToLuminanceAlpha(BitmapImageData data)
        {
            int max = data.Width * data.Height;
            byte* ptr = (byte*)data.Scan0;
            byte* copyPtr = ptr;
            for (int pos = 0; pos < max; pos++)
            {
                byte r = *ptr++;
                ptr += 2;
                byte a = *ptr++;
                *copyPtr++ = r;
                *copyPtr++ = a;
            }
            return data.Scan0;

        }

        internal static RawPixelFormat GetRawPixelFormatFromTextureFormat(TextureFormat textureFormat, bool hasAlpha)
        {
            switch (textureFormat)
            {
                case TextureFormat.Automatic:
                    return hasAlpha ? RawPixelFormat.Format16bppARGB1555 : RawPixelFormat.Format16bppRGB565;

                case TextureFormat.RGB24:
                    return RawPixelFormat.Format24bppRGB;

                case TextureFormat.RGB555: return RawPixelFormat.Format16bppRGB555;

                case TextureFormat.RGB565:
                    return RawPixelFormat.Format16bppRGB565;

                case TextureFormat.Alpha:
                case TextureFormat.HiResAlpha:
                case TextureFormat.LuminanceAlpha:
                case TextureFormat.RGBA4444:
                case TextureFormat.RGBA8888:
                    return RawPixelFormat.Format32bppARGB;

                case TextureFormat.Luminance:
                    return RawPixelFormat.Format24bppRGB;

                case TextureFormat.RGBA5551:
                    return RawPixelFormat.Format16bppARGB1555;

                default:
                    throw new NotSupportedException();

            }
        }

        public static glValueType GetValueTypeFromTextureFormat(TextureFormat textureFormat)
        {
            switch (textureFormat)
            {
                case TextureFormat.RGB555:
                    return glValueType.UnsignedShort565;

                case TextureFormat.RGB565:
                    return glValueType.UnsignedShort565;

                case TextureFormat.RGBA4444:
                case TextureFormat.RGBA8888:
                case TextureFormat.RGB24:
                case TextureFormat.LuminanceAlpha:
                case TextureFormat.Luminance:
                case TextureFormat.Alpha:
                case TextureFormat.HiResAlpha:
                    return glValueType.UnsignedByte;


                case TextureFormat.RGBA5551:
                    return glValueType.UnsignedShort5551;



                default:
                    throw new NotSupportedException();
            }
        }

        //internal static unsafe void SwapRedBluePixels(BitmapImageData data, bool isTransparent, Color transparentColor)
        //{
        //    bool isTrans = isTransparent && (transparentColor == Color.Transparent);
        //    byte r = (byte)transparentColor.R;
        //    byte g = (byte)transparentColor.G;
        //    byte b = (byte)transparentColor.B;
        //    for (int y = 0; y < data.Height; y++)
        //    {
        //        for (int x = 0; x < data.Stride; x += 4)
        //        {
        //            byte* bp = (byte*)data.Scan0 + data.Stride * y + x;
        //            byte temp = bp[0];
        //            bp[0] = bp[2];
        //            bp[2] = temp;
        //            if (!isTrans)
        //            {
        //                if (bp[0] == r && bp[1] == g && bp[2] == b)
        //                {
        //                    bp[3] = 0;
        //                }
        //                else bp[3] = 255;
        //            }
        //        }
        //    }
        //}

        internal static unsafe void Convert1555To5551(BitmapImageData data)
        {
            const short bit16 = -32768;

            short* ptr = (short*)data.Scan0;

            int max = data.Height * data.Stride / 2;
            for (int pos = 0; pos < max; pos++)
            {
                short v = ptr[0];

                int b = v & 31;
                int g = (v >> 5) & 31;
                int r = (v >> 10) & 31;
                int a = (v & bit16) != 0 ? 1 : 0;

                ptr[0] = (short)((r << 11) | (g << 6) | (b << 1) | a);
                ptr++;
            }
        }

        internal static unsafe void GenerateAlphaPlaneFromRgb565(BitmapImageData data, int w, int h)
        {
            int scan0 = (int)data.Scan0;
            for (int y = 0; y < h; y++)
            {
                int offset = data.Stride * y;
                byte* alphaBytes = (byte*)(scan0 + offset / 2);
                short* p = (short*)(scan0 + offset);
                for (int x = 0; x < w; x++)
                {
                    short pixel = *p++;
                    //*alphaBytes++ = pixel != 0 ? (byte)255 : (byte)0;
                    *alphaBytes++ = (byte)((pixel & 0x1f) * 255 / 31);
                }
            }
        }

        internal static unsafe void GenerateAlphaPlaneFromARGB(BitmapImageData data)
        {
            int h = data.Height;
            int w = data.Width;

            int scan0 = (int)data.Scan0;
            for (int y = 0; y < h; y++)
            {
                int offset = data.Stride * y;
                byte* alphaBytes = (byte*)(scan0 + offset / 4);
                byte* p = (byte*)(scan0 + offset);
                for (int x = 0; x < w; x++)
                {
                    //*alphaBytes++ = pixel != 0 ? (byte)255 : (byte)0;
                    *alphaBytes++ = *p;
                    p += 4;
                }
            }
        }


        internal static unsafe void SwapRedBluePixels(BitmapImageData data, int length)
        {
            byte* b = (byte*)data.Scan0;
            int stride = data.Stride;
            for (int y = 0; y < data.Height; y++)
            {
                for (int x = 0; x < stride; x += length)
                {
                    byte* bp = b + x;
                    byte temp = bp[0];
                    bp[0] = bp[2];
                    bp[2] = temp;
                    bp += length;
                }
                b += data.Stride;
            }
        }



        public static System.Drawing.Imaging.PixelFormat GetPixelFormatFromTextureFormat(TextureFormat format)
        {
            switch (format)
            {
                case TextureFormat.Alpha:
                case TextureFormat.RGB565:
                    return PixelFormat.Format16bppRgb565;

                case TextureFormat.RGBA5551:
                case TextureFormat.RGB555:
                    return PixelFormat.Format16bppRgb555;

                case TextureFormat.HiResAlpha:
                case TextureFormat.RGB24:
                    return PixelFormat.Format24bppRgb;

                default:
                    return PixelFormat.Format32bppRgb;
            }
        }


        internal unsafe static IntPtr GenerateAlphaPlaneFromARGB(BitmapData data, Color transparentColor, int w, int h)
        {
            GlColor glTransparent = transparentColor;
            glTransparent.A = 0;

            int stride = data.Stride >> 2;
            for (int y = 0; y < h; y++)
            {
                int* c = (int*)((int)data.Scan0) + stride * y;
                for (int x = 0; x < w; x++)
                {
                    GlColor value = *c;
                    byte r = value.B;
                    value.B = value.R;
                    value.R = r;
                    if (glTransparent == value)
                    {
                        *c = 0;
                    }
                    else
                    {
                        value.A = 255;
                        *c = value.Value;
                    }
                    c++;
                }
            }
            return data.Scan0;
        }


        internal static RawPixelFormat GetRawPixelFormatFromPixelFormat(PixelFormat pixelFormat)
        {
            switch (pixelFormat)
            {
                case PixelFormat.Format16bppRgb555: return RawPixelFormat.Format16bppRGB555;
                case PixelFormat.Format16bppRgb565: return RawPixelFormat.Format16bppRGB565;
                case PixelFormat.Format24bppRgb: return RawPixelFormat.Format24bppRGB;
                case PixelFormat.Format32bppRgb: return RawPixelFormat.Format32bppARGB;
                default: throw new NotSupportedException();
            }
        }

        internal unsafe static IntPtr ConvertRGBATo5551(BitmapData data, Color transparentColor)
        {
            int transparent = GlColor.FromColor(transparentColor).Value;
            int h = data.Height;
            int w = data.Width;
            int stride = data.Stride >> 2;
            short* bgra5551 = (short*)((int)data.Scan0);
            for (int y = 0; y < h; y++)
            {
                int* c = (int*)((int)data.Scan0) + stride * y;
                for (int x = 0; x < w; x++)
                {
                    GlColor value = *c;
                    value.A = 255;
                    if (value == transparent)
                    {
                        *bgra5551++ = 0;
                    }
                    else
                    {
                        *bgra5551++ = value.ToBGRA5551();
                    }
                    c++;
                }
            }

            return data.Scan0;
        }

        internal unsafe static IntPtr ConvertRGB555toRGBA5551(BitmapData data, Color transparentColor)
        {
            short transparent = (short)((((int)(transparentColor.B) / 8) << 10) | ((transparentColor.G / 8) << 5) | ((transparentColor.R / 8)));
            int h = data.Height;
            int w = data.Width;
            int stride = data.Stride >> 1;
            for (int y = 0; y < h; y++)
            {
                short* c = (short*)((int)data.Scan0) + stride * y;
                for (int x = 0; x < w; x++)
                {
                    short value = *c;
                    if (value == transparent)
                    {
                        *c = 0;
                    }
                    else
                    {
                        *c = GlColor.FromRGB555ToBGRA5551(value);
                    }
                    c++;
                }
            }

            return data.Scan0;
        }

    }
}
